﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;

namespace NougakudoSetupTool
{
    static class Nougakudo
    {
        const string CONFIGURATION = "configuration.xml";
        const string MSVCP100 = "msvcp100.dll";
        const string MSVCR100 = "msvcr100.dll";
        static string SYSTEM = Environment.ExpandEnvironmentVariables(@"%SystemRoot%\System32");

        internal struct Runtime
        {
            string name;
            internal string Version;
            internal string Name { get { return "NougakuDo"; } }
        }

        internal struct Application
        {
            internal string Name;
            internal string Version;
            internal string Environment;
            internal string NumOfProcessors;
            internal bool VirtualHost;  // Add for NougakuDo 1.1.0 above.
        }

        static Runtime loadRuntimeInfo;
        static Dictionary<string, Application> loadApplicationInfo;

        static string FullPath(string folder)
        {
            return Path.Combine(folder, CONFIGURATION);
        }

        static internal bool CheckConfigration(string folder)
        {
            return File.Exists(FullPath(folder));
        }

        static internal Tuple<bool, string> SaveConfiguration(string folder, Runtime runtime, Application appInfo, bool update = false)
        {

            if (!CheckConfigration(folder) && update) return new Tuple<bool,string>(false, folder + " not exsist.");
            Application[] appsInfo = new Application[] { appInfo };
            if (update)
            {
                loadApplicationInfo[appInfo.Name] = appInfo;
                appsInfo = loadApplicationInfo.Values.ToArray();
            }

            // Add for NougakuDo 1.1.0 above. A virtual host can establish only 1.
            appsInfo = CheckVirtualHost(appsInfo);

            var ret = MakeConfiguration(folder, runtime, appsInfo);
            return ret;
        }


        static internal Tuple<bool, Runtime, Dictionary<string, Application>> LoadConfiguration(string folder)
        {
            loadRuntimeInfo = new Runtime();
            loadApplicationInfo = new Dictionary<string, Application>();
            var ret = CheckConfigration(folder);
            if (!ret)
                return new Tuple<bool, Runtime, Dictionary<string, Application>>(false, loadRuntimeInfo, loadApplicationInfo);

            var doc = new XmlDocument();
            doc.Load(FullPath(folder));
            var nameNode = doc.SelectSingleNode("/configuration/runtime/name");
            loadRuntimeInfo.Version = nameNode.Attributes["version"].Value;

            var apps = doc.SelectNodes("/configuration/applications/application");
            if (apps != null)
            {
                foreach (XmlNode i in apps)
                {
                    Application app = new Application();
                    nameNode = i.SelectSingleNode("name");
                    app.Name = nameNode.InnerText;
                    app.Version = nameNode.Attributes["version"].Value;
                    var process = i.SelectSingleNode("process").InnerText;
                    if (process == "") process = "1";                   // default  // Change default
                    app.NumOfProcessors = process;
                    var environment = i.SelectSingleNode("environment").InnerText;
                    if (environment == "") environment = "production";  // default
                    app.Environment = environment;

                    // Add for NougakuDo 1.1.0 above.
                    var virtualHostNode = i.SelectSingleNode("virtualhost");
                    var virtualHost = "false";
                    bool host = false;
                    if (virtualHostNode != null)
                    {
                        virtualHost = virtualHostNode.InnerText;
                    }
                    if (virtualHost == "") virtualHost = "false";
                    bool.TryParse(virtualHost, out host);
                    app.VirtualHost = host;
                    loadApplicationInfo[app.Name] = app;
                }
            }

            return new Tuple<bool, Runtime, Dictionary<string, Application>>(true, loadRuntimeInfo, loadApplicationInfo);
        }


        static internal Tuple<bool, string> CreateZip(string source, string target, bool runtime = false)
        {
            if (!Directory.Exists(source)) return new Tuple<bool, string>(false, source + " not exsist.");
            if (!Directory.Exists(target)) return new Tuple<bool, string>(false, target + " not exsist.");

            var name = Path.GetFileName(source);
            if (runtime)
            {
                var ret1 = CopyMSVCRT(source);
                if (!ret1.Item1) return ret1;
            }

            var ret2 = ZipUtility.CreateZip(Path.Combine(target, name + ".zip"), source);
            return ret2;
        }
        

        static Tuple<bool, string> MakeConfiguration(string folder, Runtime runtime, Application[] applicationsInfo)
        {
            var doc = new XmlDocument();
            var rootNode = doc.CreateElement("configuration");
            var runtimeNode = doc.CreateElement("runtime");

            var nameNode = doc.CreateElement("name");
            nameNode.InnerText = runtime.Name;
            var versionAtt = doc.CreateAttribute("version");
            versionAtt.Value = runtime.Version;
            nameNode.Attributes.Append(versionAtt);
            runtimeNode.AppendChild(nameNode);
            
            rootNode.AppendChild(runtimeNode);
            rootNode.AppendChild(CreateApplicationElement(doc, applicationsInfo));

            var decNode = doc.CreateXmlDeclaration("1.0", "utf-8", "yes");
            doc.AppendChild(rootNode);
            doc.InsertBefore(decNode, rootNode);

            try
            {
                doc.Save(FullPath(folder));
            }
            catch (Exception e)
            {
                return new Tuple<bool,string>(false, e.Message);
            }

            return new Tuple<bool,string>(true, "");
        }

        static XmlElement CreateApplicationElement(XmlDocument doc, Application[] applicationsInfo)
        {
            var applicationsNode = doc.CreateElement("applications");
            foreach (var app in applicationsInfo)
            {
                var applicationNode = doc.CreateElement("application");
                var nameNode = doc.CreateElement("name");
                nameNode.InnerText = app.Name;
                var versionAtt = doc.CreateAttribute("version");
                versionAtt.Value = app.Version;
                nameNode.Attributes.Append(versionAtt);
                applicationNode.AppendChild(nameNode);

                var processNode = doc.CreateElement("process");
                processNode.InnerText = app.NumOfProcessors;
                applicationNode.AppendChild(processNode);

                var environmentNode = doc.CreateElement("environment");
                environmentNode.InnerText = app.Environment;
                applicationNode.AppendChild(environmentNode);

                // Add for NougakuDo 1.1.0 above.
                var virtualHostNode = doc.CreateElement("virtualhost");
                virtualHostNode.InnerText = app.VirtualHost.ToString().ToLower();
                applicationNode.AppendChild(virtualHostNode);

                applicationsNode.AppendChild(applicationNode);
            }
            return applicationsNode;
        }

        // Add for NougakuDo 1.1.0 above. A virtual host can establish only 1.
        static Application[] CheckVirtualHost(Application[] applicationsInfo)
        {
            bool useVirtualHost = false;    // Add for NougakuDo 1.1.0 above. 
            for (var i = 0; i < applicationsInfo.Length; i++)
            {
                if (useVirtualHost) applicationsInfo[i].VirtualHost = false;
                if (applicationsInfo[i].VirtualHost) useVirtualHost = true;
            }
            return applicationsInfo;
        }


        static Tuple<bool, string> CopyMSVCRT(string folder)
        {
            var tareget = Path.Combine(folder, "bin");
            if (!Directory.Exists(tareget)) return new Tuple<bool, string>(false, tareget + " not exsist.");
            var source1 = Path.Combine(SYSTEM, MSVCP100);
            if (!File.Exists(source1)) return new Tuple<bool, string>(false, "NougakuDo not installed.");
            var source2 = Path.Combine(SYSTEM, MSVCR100);
            if (!File.Exists(source2)) return new Tuple<bool, string>(false, "NougakuDo not installed.");

            try
            {
                File.Copy(source1, Path.Combine(tareget, MSVCP100), true);
                File.Copy(source2, Path.Combine(tareget, MSVCR100), true);
            }
            catch (Exception e)
            {
                return new Tuple<bool, string>(false, e.Message);
            }

            return new Tuple<bool, string>(true, "");
        }

    }
}
